module arbiter #(
    parameter DW = 16,
    parameter PORT_NUM = 4
) (
    input i_valid[PORT_NUM-1:0],
    input [DW-1:0] i_data[PORT_NUM-1:0],
    output logic i_ready[PORT_NUM-1:0],

    output logic o_valid,
    output logic [DW-1:0] o_data,
    input o_ready
);
  logic grant_vec[PORT_NUM-1:0];
  logic port_sel[PORT_NUM-1:0];
  logic [PORT_NUM-1:0] i_valid_pack;
  pack #(1, PORT_NUM) pack_i_valid (
      .din (i_valid),
      .dout(i_valid_pack)
  );
  genvar gi;
  generate
    assign grant_vec[0] = 'b1;
    assign i_ready[0]   = o_ready;
    assign port_sel[0]  = grant_vec[0] & i_valid_pack[0];
    for (gi = 1; gi < PORT_NUM; gi = gi + 1) begin : Arbiter
      assign grant_vec[gi] = ~(|i_valid_pack[gi-1:0]);
      assign i_ready[gi]   = grant_vec[gi] & o_ready;
      assign port_sel[gi]  = grant_vec[gi] & i_valid_pack[gi];
    end
    always_comb begin
      integer i;
      o_valid = 'b0;
      o_data  = 'b0;
      for (i = 0; i < PORT_NUM; i = i + 1) begin
        o_valid = o_valid | (port_sel[i] & i_valid[i]);
        o_data  = o_data | ({DW{port_sel[i]}} & i_data[i]);
      end
    end
  endgenerate

endmodule
